{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Arduino Joystick Shield Example\n",
    "\n",
    "This example shows how to use the [Sparkfun Joystick](https://www.sparkfun.com/products/9760) \n",
    "on the board. The Joystick shield contains an analog joystick which is \n",
    "connected to A0 and A1 analog channels of the Arduino connector. It also \n",
    "contains four push buttons connected at D3-D6 pins of the Arduino connector.\n",
    "\n",
    "For this notebook, an Arduino joystick shield is required."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pynq.overlays.base import BaseOverlay\n",
    "\n",
    "base = BaseOverlay(\"base.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Use Microblaze to control the joystick\n",
    "Make sure the joystick shield is plugged in. For the Microblaze to transfer\n",
    "direction or button values back, we need to define a few additional constants."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "DIRECTION_VALUE_MAP = {\n",
    "    0: 'up',\n",
    "    1: 'up_right',\n",
    "    2: 'right',\n",
    "    3: 'down_right',\n",
    "    4: 'down',\n",
    "    5: 'down_left',\n",
    "    6: 'left',\n",
    "    7: 'up_left',\n",
    "    8: 'center'\n",
    "}\n",
    "\n",
    "BUTTON_INDEX_MAP = {\n",
    "    'D3': 0,\n",
    "    'D4': 1,\n",
    "    'D5': 2,\n",
    "    'D6': 3\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The joystick can measure horizontal direction  `x` \n",
    "and vertical direction `y`.\n",
    "\n",
    "The thresholds for raw values are:\n",
    "\n",
    "Horizontal:\n",
    "\n",
    "| Threshold          | Direction    |\n",
    "| ------------------ |:------------:|\n",
    "| x < 25000          | left         |\n",
    "| 25000 < x < 39000  | center       |\n",
    "| x > 39000          | right        |\n",
    "\n",
    "Vertical:\n",
    "\n",
    "| Threshold          | Direction    |\n",
    "| ------------------ |:------------:|\n",
    "| y < 25000          | down         |\n",
    "| 25000 < y < 39000  | center       |\n",
    "| y > 39000          | up           |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "#include \"xparameters.h\"\n",
    "#include \"circular_buffer.h\"\n",
    "#include \"gpio.h\"\n",
    "#include \"xsysmon.h\"\n",
    "#include <pyprintf.h>\n",
    "\n",
    "#define X_THRESHOLD_LOW 25000\n",
    "#define X_THRESHOLD_HIGH 39000  \n",
    "\n",
    "#define Y_THRESHOLD_LOW 25000\n",
    "#define Y_THRESHOLD_HIGH 39000\n",
    "\n",
    "typedef enum directions {\n",
    "up = 0, \n",
    "right_up, \n",
    "right, \n",
    "right_down, \n",
    "down, \n",
    "left_down, \n",
    "left, \n",
    "left_up,\n",
    "centered\n",
    "}direction_e;\n",
    "\n",
    "static gpio gpio_buttons[4];\n",
    "static XSysMon SysMonInst;\n",
    "XSysMon_Config *SysMonConfigPtr;\n",
    "XSysMon *SysMonInstPtr = &SysMonInst;\n",
    "\n",
    "int init_joystick(){\n",
    "    unsigned int i, status;\n",
    "    SysMonConfigPtr = XSysMon_LookupConfig(XPAR_SYSMON_0_DEVICE_ID);\n",
    "    if(SysMonConfigPtr == NULL)\n",
    "        return -1;\n",
    "    status = XSysMon_CfgInitialize(\n",
    "        SysMonInstPtr, SysMonConfigPtr, SysMonConfigPtr->BaseAddress);\n",
    "    if(XST_SUCCESS != status)\n",
    "        return -1;\n",
    "\n",
    "    for (i=0; i<4; i++){\n",
    "        gpio_buttons[i] = gpio_open(i+3);\n",
    "        gpio_set_direction(gpio_buttons[i], GPIO_IN);\n",
    "    }\n",
    "    return 0;\n",
    "}\n",
    "\n",
    "unsigned int get_direction_value(){\n",
    "    direction_e direction;\n",
    "    unsigned int x_position, y_position;\n",
    "    while ((XSysMon_GetStatus(SysMonInstPtr) & \n",
    "            XSM_SR_EOS_MASK) != XSM_SR_EOS_MASK);\n",
    "    x_position = XSysMon_GetAdcData(SysMonInstPtr, XSM_CH_AUX_MIN+1);\n",
    "    y_position = XSysMon_GetAdcData(SysMonInstPtr, XSM_CH_AUX_MIN+9);\n",
    "\n",
    "    if (x_position > X_THRESHOLD_HIGH) {\n",
    "        if (y_position > Y_THRESHOLD_HIGH) {\n",
    "            direction = right_up;\n",
    "        } else if (y_position < Y_THRESHOLD_LOW) {\n",
    "            direction = right_down;\n",
    "        } else {\n",
    "            direction = right;\n",
    "        }\n",
    "    } else if (x_position < X_THRESHOLD_LOW) {\n",
    "        if (y_position > Y_THRESHOLD_HIGH) {\n",
    "            direction = left_up;\n",
    "        } else if (y_position < Y_THRESHOLD_LOW) {\n",
    "            direction = left_down;\n",
    "        } else {\n",
    "            direction = left;\n",
    "        }\n",
    "    } else {\n",
    "        if (y_position > Y_THRESHOLD_HIGH) {\n",
    "            direction = up;\n",
    "        } else if (y_position < Y_THRESHOLD_LOW) {\n",
    "            direction = down;\n",
    "        } else {\n",
    "            direction = centered;\n",
    "        }\n",
    "    }\n",
    "    return direction;\n",
    "}\n",
    "\n",
    "unsigned int get_button_value(unsigned int btn_i){\n",
    "    unsigned int value;\n",
    "    value = gpio_read(gpio_buttons[btn_i]);\n",
    "    return value;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Define Python wrapper for Microblaze functions\n",
    "We will also need to initialize the joystick before we can read any value.\n",
    "The following function returns `0` if the initialization is successful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "init_joystick()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following Python wrappers will call the Microblaze functions internally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def read_direction():\n",
    "    direction_value = get_direction_value()\n",
    "    return DIRECTION_VALUE_MAP[direction_value]\n",
    "\n",
    "def read_button(button):\n",
    "    return get_button_value(BUTTON_INDEX_MAP[button])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Find direction\n",
    "We can measure the direction by calling `read_direction()`.\n",
    "\n",
    "For the next cell, leave the joystick in its natural position."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'center'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "read_direction()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's pull the joystick towards the bottom right corner."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'down_right'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "read_direction()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Read button values\n",
    "\n",
    "Based on the [schematic](https://cdn.sparkfun.com/datasheets/Dev/Arduino/Shields/Joystick_Shield-v14.pdf) \n",
    "of the shield, we can see the read value will go low if the corresponding\n",
    "button has been pressed.\n",
    "\n",
    "Run the next cell while pushing both button `D4` and `D6`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Button D3 is not pressed.\n",
      "Button D4 is pressed.\n",
      "Button D5 is not pressed.\n",
      "Button D6 is pressed.\n"
     ]
    }
   ],
   "source": [
    "for button in BUTTON_INDEX_MAP:\n",
    "    if read_button(button):\n",
    "        print('Button {} is not pressed.'.format(button))\n",
    "    else:\n",
    "        print('Button {} is pressed.'.format(button))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
